# coding=utf-8
"""
author = jamon
"""
from typing import Optional

import ujson
from obespoir.websocketserver.manager import WebsocketConnectionManager

from obespoir.base.ob_handler import RegisterEvent
from obespoir.base.common_define import DEFAULT_ID, RpcMsgType
from obespoir.base.global_object import GlobalObject
from obespoir.config import NodeType
from obespoir.rpcserver.push_lib import push_message
from obespoir.share.espoir_log import global_log


async def call_target(command_id: int, data, session_id: str):
    """
    调用注册的消息处理
    :param command_id:
    :param data:
    :param session_id:
    :return:
    """
    obj = RegisterEvent.events.get(command_id, None)
    obj = obj if obj else RegisterEvent.events.get(DEFAULT_ID, None)
    # 如果没有，则进入默认消息处理
    if not obj:
        global_log.error("command_id not register", command_id=command_id)
        return

    handler = obj.get("handler")(data, command_id, session_id)
    ret = await handler.execute()
    global_log.debug("end", session_id=session_id, data=data, result=ret)
    return ret


async def rpc_message_handle(command_id: int, session_id: Optional[str],
                             data: dict):
    """
    接收到rpc消息处理
    :param session_id:
    :param command_id: int, 消息ID
    :param data: dict, 消息内容
    :return:
    """
    global_log.debug("start", command_id=command_id, data=data, node_id=GlobalObject().id)

    # 该条消息是由哪类型的节点直接传过来的（即上游最近一个节点）
    prev_node_type = data.get("prev", None)
    to = data.get("to", None)
    msg_type = data.get("msg_type")
    info = data.get("data", {})
    if not info:
        info = {}
    if to:
        if to != GlobalObject().id:
            if RpcMsgType.RESPONSE != msg_type:
                return await forwarding_next(
                    command_id, info, session_id, to, msg_type=msg_type)
            else:
                return info
        else:
            return await local_handle(command_id, info, session_id, msg_type)
    else:
        if GlobalObject().rpc_route:
            # 配置了路由信息（一般只有route类型节点才会配置）
            next_node = -1
            for node, route in GlobalObject().rpc_route.special.items():
                if command_id in route:
                    next_node = node
                    break

            if -1 == next_node:
                for node, route in GlobalObject().rpc_route.range.items():
                    for r in route:
                        if r[0] <= command_id <= r[1]:
                            next_node = node
                            break

            global_log.debug("route result", next_node=next_node,
                             cur_node_type=GlobalObject().type,
                             command_id=command_id, msg_type=msg_type)
            if -1 == next_node:
                next_node = NodeType.ROUTE
            if next_node == GlobalObject().type \
                or NodeType.ROUTE == prev_node_type \
                    and NodeType.ROUTE == next_node:
                # 如果下一个节点是自己或者该条消息是route路由节点传过来的,
                # 并且计算出来的下一个节点又是route节点，
                #   此时说明可能配置错误，防止递归，消息默认改为本地处理
                return await local_handle(
                    command_id, info, session_id, msg_type)
            else:
                return await push_message(next_node, command_id, info,
                                          session_id, to, msg_type=msg_type)
        else:   # 没有路由信息，则默认为本地处理
            return await local_handle(
                command_id, info, session_id, msg_type)


async def forwarding_next(command_id, data, session_id, to,
                          msg_type=RpcMsgType.REQUEST):
    """
    有明确目标，向其他节点转发
    :param msg_type:
    :param command_id:
    :param data:
    :param session_id:
    :param to:
    :return:None
    """
    return await push_message(None, command_id, data, session_id, to,
                              msg_type=msg_type)


async def local_handle(command_id, data, session_id, msg_type):
    """
    无需转发, 本节点处理的消息
    :param msg_type:
    :param command_id:
    :param data:
    :param session_id:
    :return: None
    """
    if NodeType.PROXY.value == GlobalObject().type:
        # proxy类型节点收到rpc消息,如果是notify类型会通过websocket推送给客户端
        if not isinstance(data, str):
            data = ujson.dumps(data)
        global_log.debug("proxy send to client", session_id=session_id,
                         data=data, command_id=command_id, msg_type=msg_type)
        seq = int(session_id.rsplit("_", 1)[-1])
        if msg_type == RpcMsgType.NOTIFY:
            # 非notify类型的消息，返回客户端是在原请求的响应中返回
            await WebsocketConnectionManager().send_message(
                seq, command_id, data)
        return data
    else:
        return await call_target(command_id, data, session_id=session_id)
